/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the GLQuake source code.

 The GLQuake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The GLQuake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the GLQuake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// dict.cpp - entity dictionary library
//


#include "game.h"


/*
 ==================
 etDict::etDict
 ==================
*/
etDict::etDict (){

	keyValues = NULL;
	glqMemory->Fill(keyValuesHashTable, 0, sizeof(keyValuesHashTable));
}

/*
 ==================
 etDict::~etDict
 ==================
*/
etDict::~etDict (){

	keyValue_t	*keyValue, *next;

	for (keyValue = keyValues; keyValue; keyValue = next){
		next = keyValue->next;

		if (keyValue->key)
			glqMemory->Free(keyValue->key);
		if (keyValue->value)
			glqMemory->Free(keyValue->value);

		glqMemory->Free(keyValue);
	}
}

/*
 ==================
 etDict::operator =
 ==================
*/
etDict &etDict::operator = (const etDict &dict){

	keyValue_t	*keyValue;

	if (this == &dict)
		return *this;

	Clear();

	for (keyValue = dict.keyValues; keyValue; keyValue = keyValue->next)
		AddKeyValue(keyValue->key, keyValue->value);

	return *this;
}

/*
 ==================
 etDict::Size
 ==================
*/
int etDict::Size () const {

	keyValue_t	*keyValue;
	int			size;

	size = sizeof(keyValues) + sizeof(keyValuesHashTable);

	for (keyValue = keyValues; keyValue; keyValue = keyValue->next){
		size += glqMemory->Size(keyValue);

		size += glqMemory->Size(keyValue->key);
		size += glqMemory->Size(keyValue->value);
	}

	return size;
}

/*
 ==================
 etDict::Clear
 ==================
*/
void etDict::Clear (){

	keyValue_t	*keyValue, *next;

	for (keyValue = keyValues; keyValue; keyValue = next){
		next = keyValue->next;

		if (keyValue->key)
			glqMemory->Free(keyValue->key);
		if (keyValue->value)
			glqMemory->Free(keyValue->value);

		glqMemory->Free(keyValue);
	}

	keyValues = NULL;
	glqMemory->Fill(keyValuesHashTable, 0, sizeof(keyValuesHashTable));
}

/*
 ==================
 etDict::Parse
 ==================
*/
bool etDict::Parse (script_t *script){

	token_t		token;
	char		key[MAX_STRING_LENGTH];
	keyValue_t	*keyValue;
	uint		hashKey;

	if (!glqParser->ReadToken(script, &token))
		return false;

	if (token.type != TT_PUNCTUATION || Str_Compare(token.string, "{")){
		glqParser->ScriptError(script, "expected '{', found '%s' instead", token.string);
		return false;
	}

	while (1){
		if (!glqParser->ReadToken(script, &token)){
			glqParser->ScriptError(script, "no matching '}' found");
			return false;
		}

		// Check for end of dictionary
		if (token.type == TT_PUNCTUATION && !Str_Compare(token.string, "}"))
			break;

		// Parse the key/value pair
		if (token.type != TT_STRING){
			glqParser->ScriptError(script, "expected quoted string, found '%s' instead", token.string);
			return false;
		}

		Str_Copy(key, token.string, sizeof(key));

		if (!glqParser->ExpectTokenType(script, &token, TT_STRING, 0)){
			glqParser->ScriptError(script, "expected quoted string, found '%s' instead", token.string);
			return false;
		}

		// Fail if the key/value pair already exists
		if (FindKeyValue(key)){
			glqParser->ScriptWarning(script, "key '%s' already defined", key);
			continue;
		}

		// Allocate a new key/value pair
		keyValue = (keyValue_t *)glqMemory->Alloc(sizeof(keyValue_t), game.memoryTag);

		keyValue->key = glqMemory->DupString(key, game.memoryTag);
		keyValue->value = glqMemory->DupString(token.string, game.memoryTag);

		// Link the key/value pair in
		keyValue->next = keyValues;
		keyValues = keyValue;

		// Add to hash table
		hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

		keyValue->nextHash = keyValuesHashTable[hashKey];
		keyValuesHashTable[hashKey] = keyValue;
	}

	return true;
}

/*
 ==================
 etDict::Write
 ==================
*/
void etDict::Write (fileHandle_t f) const {

	keyValue_t	*keyValue;

	if (keyValues == NULL)
		return;

	glqFileSystem->Printf(f, "{" NEWLINE);

	for (keyValue = keyValues; keyValue; keyValue = keyValue->next)
		glqFileSystem->Printf(f, "\"%s\" \"%s\"" NEWLINE, keyValue->key, keyValue->value);

	glqFileSystem->Printf(f, "}" NEWLINE);
}

/*
 ==================
 etDict::ParseKeyValue
 ==================
*/
bool etDict::ParseKeyValue (script_t *script){

	token_t		token;
	char		key[MAX_STRING_LENGTH];
	keyValue_t	*keyValue;
	uint		hashKey;

	// Parse the key
	if (!glqParser->ExpectTokenType(script, &token, TT_STRING, 0)){
		glqParser->ScriptError(script, "expected quoted string, found '%s' instead", token.string);
		return false;
	}

	Str_Copy(key, token.string, sizeof(key));

	// Parse the value
	if (!glqParser->ExpectTokenType(script, &token, TT_STRING, 0)){
		glqParser->ScriptError(script, "expected quoted string, found '%s' instead", token.string);
		return false;
	}

	// Fail if the key/value pair already exists
	if (FindKeyValue(key)){
		glqParser->ScriptWarning(script, "key '%s' already defined", key);
		return true;
	}

	// Allocate a new key/value pair
	keyValue = (keyValue_t *)glqMemory->Alloc(sizeof(keyValue_t), game.memoryTag);

	keyValue->key = glqMemory->DupString(key, game.memoryTag);
	keyValue->value = glqMemory->DupString(token.string, game.memoryTag);

	// Link the key/value pair in
	keyValue->next = keyValues;
	keyValues = keyValue;

	// Add to hash table
	hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

	keyValue->nextHash = keyValuesHashTable[hashKey];
	keyValuesHashTable[hashKey] = keyValue;

	return true;
}

/*
 ==================
 etDict::WriteKeyValues
 ==================
*/
void etDict::WriteKeyValues (fileHandle_t f) const {

	keyValue_t	*keyValue;

	if (keyValues == NULL)
		return;

	for (keyValue = keyValues; keyValue; keyValue = keyValue->next)
		glqFileSystem->Printf(f, "\"%s\" \"%s\"" NEWLINE, keyValue->key, keyValue->value);
}

/*
 ==================
 etDict::ListKeyValues
 ==================
*/
void etDict::ListKeyValues () const {

	keyValue_t	*keyValue;

	for (keyValue = keyValues; keyValue; keyValue = keyValue->next)
		G_Printf("%-32s \"%s" S_COLOR_DEFAULT "\"\n", keyValue->key, keyValue->value);
}

/*
 ==================
 etDict::NumKeyValues
 ==================
*/
int etDict::NumKeyValues () const {

	keyValue_t	*keyValue;
	int			num = 0;

	for (keyValue = keyValues; keyValue; keyValue = keyValue->next)
		num++;

	return num;
}

/*
 ==================
 etDict::GetKeyValue
 ==================
*/
const keyValue_t *etDict::GetKeyValue (int index) const {

	keyValue_t	*keyValue;
	int			num = 0;

	for (keyValue = keyValues; keyValue; keyValue = keyValue->next){
		if (num == index)
			break;

		num++;
	}

	return keyValue;
}

/*
 ==================
 etDict::AddKeyValue
 ==================
*/
void etDict::AddKeyValue (const char *key, const char *value){

	keyValue_t	*keyValue;
	uint		hashKey;

	// Fail if the key/value pair already exists
	hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

	for (keyValue = keyValuesHashTable[hashKey]; keyValue; keyValue = keyValue->nextHash){
		if (!Str_ICompare(keyValue->key, key))
			return;
	}

	// Allocate a new key/value pair
	keyValue = (keyValue_t *)glqMemory->Alloc(sizeof(keyValue_t), game.memoryTag);

	keyValue->key = glqMemory->DupString(key, game.memoryTag);
	keyValue->value = glqMemory->DupString(value, game.memoryTag);

	// Link the key/value pair in
	keyValue->next = keyValues;
	keyValues = keyValue;

	// Add to hash table
	keyValue->nextHash = keyValuesHashTable[hashKey];
	keyValuesHashTable[hashKey] = keyValue;
}

/*
 ==================
 etDict::RemoveKeyValue
 ==================
*/
void etDict::RemoveKeyValue (const char *key){

	keyValue_t	*keyValue, **prevKeyValue;
	keyValue_t	*hash, **prevHash;
	uint		hashKey;

	// Find the key/value pair
	prevKeyValue = &keyValues;

	while (1){
		keyValue = *prevKeyValue;
		if (!keyValue)
			break;		// Not found

		if (!Str_ICompare(keyValue->key, key)){
			// Remove from hash table
			hashKey = Str_HashKey(keyValue->key, KEY_VALUES_HASH_SIZE, false);

			prevHash = &keyValuesHashTable[hashKey];

			while (1){
				hash = *prevHash;
				if (!hash)
					break;		// Not found

				if (!Str_ICompare(hash->key, keyValue->key)){
					*prevHash = hash->nextHash;
					break;
				}

				prevHash = &hash->nextHash;
			}

			// Remove the key/value pair
			*prevKeyValue = keyValue->next;

			if (keyValue->key)
				glqMemory->Free(keyValue->key);
			if (keyValue->value)
				glqMemory->Free(keyValue->value);

			glqMemory->Free(keyValue);

			return;
		}

		prevKeyValue = &keyValue->next;
	}
}

/*
 ==================
 etDict::FindKeyValue
 ==================
*/
const keyValue_t *etDict::FindKeyValue (const char *key) const {

	keyValue_t	*keyValue;
	uint		hashKey;

	hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

	for (keyValue = keyValuesHashTable[hashKey]; keyValue; keyValue = keyValue->nextHash){
		if (!Str_ICompare(keyValue->key, key))
			return keyValue;
	}

	return NULL;
}

/*
 ==================
 etDict::FindKeyValue
 ==================
*/
keyValue_t *etDict::FindKeyValue (const char *key){

	keyValue_t	*keyValue;
	uint		hashKey;

	hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

	for (keyValue = keyValuesHashTable[hashKey]; keyValue; keyValue = keyValue->nextHash){
		if (!Str_ICompare(keyValue->key, key))
			return keyValue;
	}

	return NULL;
}

/*
 ==================
 etDict::MatchPrefix
 ==================
*/
const keyValue_t *etDict::MatchPrefix (const char *prefix, const keyValue_t *lastMatch) const {

	keyValue_t	*keyValue;
	int			len;

	len = Str_Length(prefix);

	if (lastMatch == NULL)
		keyValue = keyValues;
	else
		keyValue = lastMatch->next;

	for ( ; keyValue; keyValue = keyValue->next){
		if (!Str_ICompareChars(keyValue->key, prefix, len))
			return keyValue;
	}

	return NULL;
}

/*
 ==================
 etDict::SetString
 ==================
*/
void etDict::SetString (const char *key, const char *value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value);
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value, game.memoryTag);
}

/*
 ==================
 etDict::SetBool
 ==================
*/
void etDict::SetBool (const char *key, bool value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, Str_FromInteger(value != false));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(Str_FromInteger(value != false), game.memoryTag);
}

/*
 ==================
 etDict::SetInteger
 ==================
*/
void etDict::SetInteger (const char *key, int value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, Str_FromInteger(value));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(Str_FromInteger(value), game.memoryTag);
}

/*
 ==================
 etDict::SetFloat
 ==================
*/
void etDict::SetFloat (const char *key, float value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, Str_FromFloat(value, -2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(Str_FromFloat(value, -2), game.memoryTag);
}

/*
 ==================
 etDict::SetVec2
 ==================
*/
void etDict::SetVec2 (const char *key, const glqVec2 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value.ToString(-2), game.memoryTag);
}

/*
 ==================
 etDict::SetVec3
 ==================
*/
void etDict::SetVec3 (const char *key, const glqVec3 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value.ToString(-2), game.memoryTag);
}

/*
 ==================
 etDict::SetVec4
 ==================
*/
void etDict::SetVec4 (const char *key, const glqVec4 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value.ToString(-2), game.memoryTag);
}

/*
 ==================
 etDict::SetVec5
 ==================
*/
void etDict::SetVec5 (const char *key, const glqVec5 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value.ToString(-2), game.memoryTag);
}

/*
 ==================
 etDict::SetAngles
 ==================
*/
void etDict::SetAngles (const char *key, const glqAngles &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value.ToString(-2), game.memoryTag);
}

/*
 ==================
 etDict::SetQuat
 ==================
*/
void etDict::SetQuat (const char *key, const glqQuat &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value.ToString(-2), game.memoryTag);
}

/*
 ==================
 etDict::SetMat3
 ==================
*/
void etDict::SetMat3 (const char *key, const glqMat3 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value.ToString(-2), game.memoryTag);
}

/*
 ==================
 etDict::SetMat4
 ==================
*/
void etDict::SetMat4 (const char *key, const glqMat4 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-2));
		return;
	}

	if (keyValue->value)
		glqMemory->Free(keyValue->value);

	keyValue->value = glqMemory->DupString(value.ToString(-2), game.memoryTag);
}

/*
 ==================
 etDict::GetString
 ==================
*/
bool etDict::GetString (const char *key, const char **value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		*value = defaultValue;
		return false;
	}

	*value = keyValue->value;

	return true;
}

/*
 ==================
 etDict::GetBool
 ==================
*/
bool etDict::GetBool (const char *key, bool *value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		*value = Str_ToInteger(defaultValue) != 0;
		return false;
	}

	*value = Str_ToInteger(keyValue->value) != 0;

	return true;
}

/*
 ==================
 etDict::GetInteger
 ==================
*/
bool etDict::GetInteger (const char *key, int *value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		*value = Str_ToInteger(defaultValue);
		return false;
	}

	*value = Str_ToInteger(keyValue->value);

	return true;
}

/*
 ==================
 etDict::GetFloat
 ==================
*/
bool etDict::GetFloat (const char *key, float *value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		*value = Str_ToFloat(defaultValue);
		return false;
	}

	*value = Str_ToFloat(keyValue->value);

	return true;
}

/*
 ==================
 etDict::GetVec2
 ==================
*/
bool etDict::GetVec2 (const char *key, glqVec2 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f", &value.x, &value.y);
		return false;
	}

	sscanf(keyValue->value, "%f %f", &value.x, &value.y);

	return true;
}

/*
 ==================
 etDict::GetVec3
 ==================
*/
bool etDict::GetVec3 (const char *key, glqVec3 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f", &value.x, &value.y, &value.z);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f", &value.x, &value.y, &value.z);

	return true;
}

/*
 ==================
 etDict::GetVec4
 ==================
*/
bool etDict::GetVec4 (const char *key, glqVec4 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f", &value.x, &value.y, &value.z, &value.w);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f", &value.x, &value.y, &value.z, &value.w);

	return true;
}

/*
 ==================
 etDict::GetVec5
 ==================
*/
bool etDict::GetVec5 (const char *key, glqVec5 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f %f", &value.x, &value.y, &value.z, &value.s, &value.t);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f %f", &value.x, &value.y, &value.z, &value.s, &value.t);

	return true;
}

/*
 ==================
 etDict::GetAngles
 ==================
*/
bool etDict::GetAngles (const char *key, glqAngles &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f", &value.pitch, &value.yaw, &value.roll);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f", &value.pitch, &value.yaw, &value.roll);

	return true;
}

/*
 ==================
 etDict::GetQuat
 ==================
*/
bool etDict::GetQuat (const char *key, glqQuat &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Identity();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f", &value.x, &value.y, &value.z, &value.w);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f", &value.x, &value.y, &value.z, &value.w);

	return true;
}

/*
 ==================
 etDict::GetMat3
 ==================
*/
bool etDict::GetMat3 (const char *key, glqMat3 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Identity();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f %f %f %f %f %f", &value[0][0], &value[0][1], &value[0][2], &value[1][0], &value[1][1], &value[1][2], &value[2][0], &value[2][1], &value[2][2]);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f %f %f %f %f %f", &value[0][0], &value[0][1], &value[0][2], &value[1][0], &value[1][1], &value[1][2], &value[2][0], &value[2][1], &value[2][2]);

	return true;
}

/*
 ==================
 etDict::GetMat4
 ==================
*/
bool etDict::GetMat4 (const char *key, glqMat4 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Identity();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", &value[0][0], &value[0][1], &value[0][2], &value[0][3], &value[1][0], &value[1][1], &value[1][2], &value[1][3], &value[2][0], &value[2][1], &value[2][2], &value[2][3], &value[3][0], &value[3][1], &value[3][2], &value[3][3]);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", &value[0][0], &value[0][1], &value[0][2], &value[0][3], &value[1][0], &value[1][1], &value[1][2], &value[1][3], &value[2][0], &value[2][1], &value[2][2], &value[2][3], &value[3][0], &value[3][1], &value[3][2], &value[3][3]);

	return true;
}